1 module websocket_connection;
2 import hip.api.net.server;
3 import hip.api.net.utils;
4 import std.stdio;
5 import core.sync.mutex;
6 import handy_httpd;
7 import hipengine_commands;
8 
9 struct Connection
10 {
11 	uint id;
12     WebSocketConnection socket;
13 }
14 public __gshared Connection*[] connections;
15 
16 class HipremeEngineWebSocketServer: WebSocketMessageHandler
17 {
18     // ctx.server.stop(); // Calling stop will gracefully shutdown the server.
19     this()
20     {
21         wsMutex = new shared Mutex;
22     }
23 
24     private uint wsID;
25 
26     override void onConnectionEstablished(WebSocketConnection conn)
27     {
28         // synchronized(wsMutex)
29         {
30             Connection* c;
31             if(freeIdsList.length)
32             {
33                 uint last = freeIdsList[$-1];
34                 freeIdsList = freeIdsList[0..$-1];
35                 c = connections[last];
36                 c.socket = conn;
37                 writeln("Socket ID ", NetID.start + c.id, " reacquired");
38             }
39             else
40             {
41                 connections~= c = new Connection(id, conn);
42                 wsID = id++;
43             }
44             uint data = NetID.start + c.id;
45             writeln("Socket ID ", data, " connected. Returning its ID.");
46             conn.sendBinaryMessage(toBytes(data));
47         }
48     }
49 
50     override void onTextMessage(WebSocketTextMessage msg)
51     {
52         import std.stdio;
53         import std.algorithm.searching;
54 
55         string text = msg.payload; //Irrelevant here
56 
57         // infoF!"Got TEXT: %s"(msg.payload);
58     }
59 
60     static bool sendBinaryToSocket(uint toSocketID, ubyte[] data)
61     {
62         if(toSocketID >= connections.length)
63         {
64             writeln("toSocketID [", toSocketID, "] is out of range");
65             return false;
66         }
67 
68         connections[toSocketID].socket.sendBinaryMessage(data);
69         return true;
70     }
71 
72     /**
73      * Specification:
74      * 4 bytes is the from socket ID
75      * 4 bytes is the to socket ID
76      *
77      * Remaining bytes are the data frame
78      *
79      * Params:
80      *   msg =
81      */
82     override void onBinaryMessage(WebSocketBinaryMessage msg)
83     {
84         import std.stdio;
85 
86         uint fromSocketID = *cast(uint*)msg.payload[0..4] - NetID.start;
87         uint toSocketID = *cast(uint*)msg.payload[4..8];
88 
89         ubyte[] data = fromNetworkBytes(msg.payload[8..$])[NetHeader.sizeof..$];
90 
91         switch(toSocketID)
92         {
93             case NetID.server:
94 
95                 ubyte command = *cast(ubyte*)data.ptr;
96                 writeln("Received command ID [", command, "] from connection ", fromSocketID, " Buffer is ", data);
97                 switch(command)
98                 {
99                     case MarkedNetReservedTypes.connect:
100                         //Just pong it back to say it has connected.
101                         sendBinaryToSocket(fromSocketID, getNetworkFormattedData(MarkedNetReservedTypes.connect));
102                         break;
103                     case MarkedNetReservedTypes.get_connected_clients:
104                         auto response = getNetworkFormattedData(getConnectedClients(fromSocketID), MarkedNetReservedTypes.get_connected_clients);
105                         sendBinaryToSocket(fromSocketID, response);
106                         break;
107                     case MarkedNetReservedTypes.client_connect:
108                         uint targetID = *cast(uint*)(data.ptr+ubyte.sizeof); //After the command, the targetID
109                         sendBinaryToSocket(targetID - NetID.start, getNetworkFormattedData(ConnectToClientResponse(fromSocketID), MarkedNetReservedTypes.client_connect));
110                         break;
111                     default:
112 
113                         writeln("Invalid Command Received. Buffer: ", data);
114                         break;
115                 }
116                 break;
117             case NetID.broadcast:
118                 writeln(fromSocketID, " is broadcasting!");
119                 foreach(c; connections)
120                 {
121                     if(c.id != fromSocketID)
122                         c.socket.sendBinaryMessage(data);
123                 }
124                 break;
125             default:
126                 toSocketID-= NetID.start;
127                 if(connections.length <= toSocketID)
128                     writeln("Socket tried sending data to invalid socket ID ", toSocketID + NetID.start);
129                 else
130                     connections[toSocketID].socket.sendBinaryMessage(data);
131                 break;
132 
133         }
134         // writeln("Socket ", fromSocketID, " is sending ", data.length, " bytes to socket ", toSocketID);
135 
136     }
137 
138     override void onCloseMessage(WebSocketCloseMessage msg)
139     {
140         import std.algorithm.searching;
141         import std.conv:to;
142         synchronized
143         {
144             ptrdiff_t index = countUntil!((Connection* c) => c.socket == msg.conn)(connections);
145             if(index != -1)
146             {
147                 writeln("Socket ", NetID.start + connections[index].id, " closed");
148                 connections[index].socket = null;
149                 freeIdsList~= index.to!uint;
150             }
151         }
152     }
153 }
154 
155 private __gshared uint id;
156 
157 private __gshared uint[] freeIdsList;
158 private shared Mutex wsMutex;